/**
 ** 实现指令重命名的寄存器组，重命名过程需按序遵循如下3原则
 ** 1. 源分配    <==>  查询对应链，将源寄存器号重命名为链中最后一元素块号，链长度>1时，元素未准备好
 ** 2. 目标分配  <==>  空闲块队列中，输出一新块号，连接在对应链的后面
 ** 3. 更新策略  <==>  当某一个块NX(i)更新时，此块前若有其他块NX(i-1)，则释放NX(i-1)为空闲块
 ** 将ROB也融合到寄存器中，这样做的好处是，可以避免模块之间传输不定多组的数据
 ** 直白一点说，就是ROB可能一次传递过来很多组内容
**/
//可能并不需要输出，源寄存器的映射后的号，只需要具体的值和是否准备好

module registers (
    input wire clk,
    input wire rst,
    input wire [4:0] rs_1, rs_2, rt_1, rt_2,  //需要重命名的源寄存器号
    input wire [4:0] rd_1, rd_2,              //需要重命名的目标寄存器号
    input wire [31:0] imm_1, imm_2,
    input wire rob_save_direct_ctrl_1, rob_save_direct_ctrl_2,
    input wire alu_commit_en, mul_commit_en, div_commit_en, mem_commit_en,
    input wire [4:0] alu_commit_rob_no, mul_commit_rob_no, div_commit_rob_no, mem_commit_rob_no,
    input wire [31:0] alu_commit_value, mul_commit_value, div_commit_value, mem_commit_value,
    input wire [4:0] jal_reg_no,              //跳转指令需要得到的寄存器值 
    output reg [31:0] jal_reg_value,
    output reg [31:0] dm_value_1, dm_value_2, //对于load指令而言，要写入到寄存器中的值
    output reg rs_1_ready, rt_1_ready,        //得到的源寄存器是否就绪的状态
    output reg rs_2_ready, rt_2_ready,
    output reg [31:0] rs_1_value, rt_1_value, //得到的源寄存器的内容
    output reg [31:0] rs_2_value, rt_2_value,
    output reg [4:0] rs_rob_no_1, rt_rob_no_1, rs_rob_no_2, rt_rob_no_2,
    output reg [4:0] rd_rob_no_1, rd_rob_no_2
);
    //32个32位宽的逻辑寄存器
    reg [31:0] logic_reg [0:31];
    //64个32位宽的物理寄存器
    reg [31:0] real_reg [0:63];
    //物理寄存器是否已经写回的标志位
    reg [63:0] real_reg_ok; 
    //定义专用寄存器方便跳转指令jalr使用
    reg [31:0] jal_reg [0:5];
    //物理块空闲队列
    reg [5:0] real_free_queue [0:63];
    reg [5:0] free_head;
    reg [5:0] free_tail;
    //32个逻辑寄存器的映射关系表
    reg [5:0] mapping [0:31][0:15];
    //关系映射表是否在用的标记
    reg [15:0] mapping_use [0:31];
    //映射关系表的头尾指针
    reg [5:0] line_head [0:31];
    reg [5:0] line_tail [0:31];
    reg [5:0] line_count_temp_1, line_count_temp_2;
    //根据rob号检索到物理块号的数组
    reg [5:0] rob_real_no [0:31];
    //根据rob接纳所到逻辑块号的数组
    reg [4:0] rob_logic_no [0:31]; 
    //根据物理块号，查询到rob号的数组
    reg [4:0] real_rob_no [0:63]; 
    //根据物理块号，查询到自身在映射mapping中的位置
    //reg [5:0] real_mapping_index_no [0:63];
    //暂存写入ROB的寄存器值
    reg [31:0] rob_value [0:31];
    //rob中条目是否已经就绪的标志位
    reg [31:0] rob_ok;
    //rob条目是否在使用的标志位
    reg [31:0] rob_use;
    //rob所要用到的指针
    reg [4:0] rob_tail, rob_head;
    reg [5:0] alu_real_reg_no, mul_real_reg_no, div_real_reg_no;
    reg [5:0] alu_logic_reg_no, mul_logic_reg_no, div_logic_reg_no;
    //一些关于寄存器号的中间信号
    reg [4:0] rd_1_mp, rd_2_mp;
    reg [4:0] rs_1_mp, rt_1_mp, rs_2_mp, rt_2_mp; 
    reg reg_update_en;
    reg [4:0] rob_temp_1, rob_temp_2;

    integer i, j, k;
    
    // 初始化关系映射表
    initial begin
        //初始化空闲块队列
        free_head = 0;
        free_tail = 63;
        //初始化rob
        rob_head = 0;
        rob_tail = 0;
        reg_update_en = 0;
        rob_ok = 32'h00000000;
        rob_use = 32'h00000000;
        real_reg_ok = 64'h0000000000000000;
        for (i=0; i<64; i=i+1) begin
            if (i < 32) begin
                //将每一个映射链头尾指针都初始化成0，即当前映射关系表为空
                line_head[i] = 0;
                line_tail[i] = 0;
                mapping_use[i] = 16'h0000;
            end
            //初始化空闲队列，记得不要初始化0号块，0号表示寄存器无效
            real_free_queue[i] = i;
        end
    end

    //读取跳转指令专用寄存器的值
    always @(jal_reg_no) begin
        jal_reg_value = jal_reg[jal_reg_no];
    end

    //重命名逻辑
    always @(rs_1 or rt_1 or rd_1 or rs_2 or rt_2 or rd_2 or imm_1 or imm_2) begin
        //给第一个指令的源寄存器完成映射
        if (rs_1>0) begin
            rs_1_mp = mapping[rs_1][line_tail[rs_1]-1];
            //检查当前链的长度是否为1，来判定寄存器是否可用
            if (line_tail[rs_1]>=line_head[rs_1]) begin
                line_count_temp_1 = line_tail[rs_1]-line_head[rs_1];
            end
            else begin
                line_count_temp_1 = line_tail[rs_1]-line_head[rs_1]+16;
            end
            if (line_count_temp_1==1 && real_reg_ok[rs_1_mp]==1) begin
                rs_1_ready = 1;
                rs_1_value = real_reg[rs_1_mp];
            end
            else begin
                //判断ROB是否有值
                rob_temp_1 = real_rob_no[rs_1_mp];
                if (rob_ok[rob_temp_1]==1) begin
                    rs_1_ready = 1;
                    rs_1_value = rob_value[rob_temp_1];
                end
                else begin
                    rs_1_ready = 0;
                end
            end
            rs_rob_no_1 = real_rob_no[rs_1_mp];
        end
        else begin
            rs_1_ready = 0;
            rs_1_value = 0;
            rs_1_mp = 0;
            rs_rob_no_1 = 0;
        end

        if (rt_1>0) begin
            rt_1_mp = mapping[rt_1][line_tail[rt_1]-1];
            
            //检查当前链的长度是否为1，来判定寄存器是否可用
            if (line_tail[rt_1]>=line_head[rt_1]) begin
                line_count_temp_2 = line_tail[rt_1]-line_head[rt_1];
            end
            else begin
                line_count_temp_2 = line_tail[rt_1]-line_head[rt_1]+16;
            end
            //长度为1，并且已经写回才行
            if (line_count_temp_2==1 && real_reg_ok[rt_1_mp]==1) begin
                rt_1_ready = 1;
                rt_1_value = real_reg[rt_1_mp];
            end
            else begin
                //判断ROB是否有值
                rob_temp_2 = real_rob_no[rt_1_mp];
                if (rob_ok[rob_temp_2]==1) begin
                    rt_1_ready = 1;
                    rt_1_value = rob_value[rob_temp_2];
                end
                else begin
                    rt_1_ready = 0;
                end
            end
            //输出源寄存器对应的rob号
            rt_rob_no_1 = real_rob_no[rt_1_mp];
        end
        else begin
            rt_1_mp = 0;
            rt_rob_no_1 = 0;
            rt_1_ready = 0;
            rt_1_value = 0;
        end

        //$display("寄存器rs: ", rs_1, "取值: ", rs_1_value, "寄存器rt: ", rt_1, "取值: ", rt_1_value);
        if (rd_1>0) begin
            //取出空闲块，分配给指令的目标寄存器RD
            rd_1_mp = real_free_queue[free_head];
            free_head = (free_head+1)%64;
            //将映射关系写入到映射表
            //real_mapping_index_no[rd_1_mp] = line_tail[rd_1];
            mapping[rd_1][line_tail[rd_1]] = rd_1_mp;
            mapping_use[rd_1][line_tail[rd_1]] = 1;
            line_tail[rd_1] = (line_tail[rd_1]+1)%16;

            if (rob_save_direct_ctrl_1==1) begin
                //直接把立即数写入到物理块
                //$display("映射队列号: ", rd_1, ", 队列长度: ", (line_tail[rd_1]-line_head[rd_1]));
                //$display("将内容: ",imm_1," 写入到逻辑块: ",rd_1, " 对应物理块:", rd_1_mp);
                real_reg[rd_1_mp] = imm_1;
                real_reg_ok[rd_1_mp] = 1;

                for (j=0; j<32; j=j+1) begin
                    if (mapping[rd_1][j]==rd_1_mp && mapping_use[rd_1][j]==1) begin
                        if (j==(line_head[rd_1]+1)%16) begin
                            //将前一个块释放掉，块号加入到空闲队列
                            free_tail = (free_tail+1)%64;
                            real_free_queue[free_tail] = mapping[rd_1][j-1];
                            mapping_use[rd_1][j-1] = 0;
                            //将链头后移，更新链的长度
                            line_head[rd_1] = (line_head[rd_1]+1)%16;
                            //$display("更新寄存器: ", alu_logic_reg_no, " 头指针: ", line_head[alu_logic_reg_no], " 尾指针: ", line_tail[alu_logic_reg_no], " 更新完链长度: ", line_tail[alu_logic_reg_no]-line_head[alu_logic_reg_no]);
                            //$display("下标为: ", j, " 更新寄存器: ", alu_logic_reg_no, " 更新完的队列尾指针: ", line_tail[alu_logic_reg_no], " 头指针: ", line_head[alu_logic_reg_no]);
                        end
                    end
                end
            end
            else begin
                //将物理块写入到rob
                if ((rob_tail+1)%32 != rob_head) begin
                    rd_rob_no_1 = rob_tail;
                    rob_real_no[rob_tail] = rd_1_mp;
                    rob_logic_no[rob_tail] = rd_1;
                    real_rob_no[rd_1_mp] = rob_tail;
                    rob_ok[rob_tail] = 0;
                    rob_use[rob_tail] = 1;
                    //rob指针向后移动
                    rob_tail = (rob_tail+1)%32;
                    //$display("R", rd_1, " R", rs_1, " R", rt_1, " --> ", "R", rd_1_mp, " R", rs_1_mp, " R", rt_1_mp, " --> final-ROB: ", rd_rob_no_1);
                end
                else begin
                    $display("ROB满警告，系统已经无法正常处理任务");
                end
            end
        end
        
        //给第二个指令的源寄存器映射，需要检查rs和rt是否和第一条指令的rd相等
        if (rs_2>0) begin
            rs_2_mp = mapping[rs_2][line_tail[rs_2]-1];
            //检查当前链的长度是否为1，来判定寄存器是否可用
            if (line_tail[rs_2]>=line_head[rs_2]) begin
                line_count_temp_1 = line_tail[rs_2]-line_head[rs_2];
            end
            else begin
                line_count_temp_1 = line_tail[rs_2]-line_head[rs_2]+16;
            end
            if (line_count_temp_1==1 && real_reg_ok[rs_2_mp]==1) begin
                rs_2_ready = 1;
                rs_2_value = real_reg[rs_2_mp];
            end
            else begin
                rob_temp_1 = real_rob_no[rs_2_mp];
                if (rob_ok[rob_temp_1]==1) begin
                    rs_2_ready = 1;
                    rs_2_value = rob_value[rob_temp_1];
                end
                else begin
                    rs_2_ready = 0;
                end
            end
            rs_rob_no_2 = real_rob_no[rs_2_mp];
        end
        else begin
            rs_2_mp = 0;
            rs_rob_no_2 = 0;
            rs_2_ready = 0;
            rs_2_value = 0;
        end

        if (rt_2>0) begin
            rt_2_mp = mapping[rt_2][line_tail[rt_2]-1];
            //检查当前链的长度是否为1，来判定寄存器是否可用
            if (line_tail[rt_2]>=line_head[rt_2]) begin
                line_count_temp_2 = line_tail[rt_2]-line_head[rt_2];
            end
            else begin
                line_count_temp_2 = line_tail[rt_2]-line_head[rt_2]+16;
            end
            if (line_count_temp_2==1  && real_reg_ok[rt_2_mp]==1) begin
                rt_2_ready = 1;
                rt_2_value = real_reg[rt_2_mp];
            end
            else begin
                rob_temp_2 = real_rob_no[rt_2_mp];
                if (rob_ok[rob_temp_2]==1) begin
                    rt_2_ready = 1;
                    rt_2_value = rob_value[rob_temp_2];
                end
                else begin
                    rt_2_ready = 0;
                end
            end
            rt_rob_no_2 = real_rob_no[rt_2_mp];
        end
        else begin
            rt_2_ready = 0;
            rt_2_value = 0;
            rt_2_mp = 0;
            rt_rob_no_2 = 0;
        end

        //$display("寄存器rs: ", rs_2, "取值: ", rs_2_value, "寄存器rt: ", rt_2, "取值: ", rt_2_value);
        if (rd_2 > 0) begin
            //取出空闲块，分配给指令的目标寄存器RD
            rd_2_mp = real_free_queue[free_head];
            free_head = (free_head+1)%64;
            //将映射关系写入到映射表
            //real_mapping_index_no[rd_2_mp] = line_tail[rd_2];
            mapping[rd_2][line_tail[rd_2]] = rd_2_mp;
            mapping_use[rd_2][line_tail[rd_2]] = 1;
            line_tail[rd_2] = (line_tail[rd_2]+1)%16;
            //$display("原逻辑块: ", rd_2);
            if (rob_save_direct_ctrl_2==1) begin
                real_reg[rd_2_mp] = imm_2;
                real_reg_ok[rd_2_mp] = 1;
                //$display("原逻辑块: ", rd_2, " 分配的映射块: ", rd_2_mp);
                for (j=0; j<32; j=j+1) begin
                    if (mapping[rd_2][j]==rd_2_mp && mapping_use[rd_2][j]==1) begin
                        if (j==(line_head[rd_2]+1)%16) begin
                            //将前一个块释放掉，块号加入到空闲队列
                            free_tail = (free_tail+1)%64;
                            real_free_queue[free_tail] = mapping[rd_2][j-1];
                            mapping_use[rd_2][j-1] = 0;
                            //将链头后移，更新链的长度
                            //$display("释放: ", mapping[rd_2][j-1]);
                            line_head[rd_2] = (line_head[rd_2]+1)%16;
                            //$display("更新寄存器: ", alu_logic_reg_no, " 头指针: ", line_head[alu_logic_reg_no], " 尾指针: ", line_tail[alu_logic_reg_no], " 更新完链长度: ", line_tail[alu_logic_reg_no]-line_head[alu_logic_reg_no]);
                            //$display("下标为: ", j, " 更新寄存器: ", alu_logic_reg_no, " 更新完的队列尾指针: ", line_tail[alu_logic_reg_no], " 头指针: ", line_head[alu_logic_reg_no]);
                        end
                    end
                end
            end
            else begin
                //将rob还有空位时，物理块写入到rob
                if ((rob_tail+1)%32 != rob_head) begin
                    rd_rob_no_2 = rob_tail;
                    rob_real_no[rob_tail] = rd_2_mp;
                    rob_logic_no[rob_tail] = rd_2;
                    real_rob_no[rd_2_mp] = rob_tail;
                    rob_ok[rob_tail] = 0;
                    rob_use[rob_tail] = 1;
                    //rob指针向后移动
                    rob_tail = (rob_tail+1)%32;
                    //$display("R", rd_2, " R", rs_2, " R", rt_2, " --> ", "R", rd_2_mp, " R", rs_2_mp, " R", rt_2_mp, " --> final-ROB: ", rd_rob_no_2);
                end
                else begin
                    $display("ROB满警告，系统已经无法正常处理任务");
                end
            end
        end
        
    end
    
    //加法运算结果写回ROB
    always @(alu_commit_en or alu_commit_rob_no or alu_commit_value) begin
        if (alu_commit_en) begin
            rob_value[alu_commit_rob_no] = alu_commit_value;
            rob_ok[alu_commit_rob_no] = 1;
            //修改更新逻辑的值，触发更新逻辑
            reg_update_en = ~reg_update_en;
        end
    end

    always @(mul_commit_en or mul_commit_rob_no or mul_commit_value) begin
        if (mul_commit_en) begin
            rob_value[mul_commit_rob_no] = mul_commit_value;
            rob_ok[mul_commit_rob_no] = 1;
            //修改更新逻辑的值，触发更新逻辑
            reg_update_en = ~reg_update_en;
        end
    end

    always @(div_commit_en or div_commit_rob_no or div_commit_value) begin
        if (div_commit_en) begin
            rob_value[div_commit_rob_no] = div_commit_value;
            rob_ok[div_commit_rob_no] = 1;
            //修改更新逻辑的值，触发更新逻辑
            reg_update_en = ~reg_update_en;
        end
    end

    always @(mem_commit_en or mem_commit_rob_no or mem_commit_value) begin
        if (mem_commit_en) begin
            rob_value[mem_commit_rob_no] = mem_commit_value;
            rob_ok[mem_commit_rob_no] = 1;
            //修改更新逻辑的值，触发更新逻辑
            reg_update_en = ~reg_update_en;
        end
    end

    //提交数据到寄存器的逻辑
    always @(reg_update_en) begin
        for (i=0; i<32; i=i+1) begin
            //对于已经成功写回的ROB，检查其是否可以提交
            if (rob_use[i]==1 && rob_ok[i]==1) begin
                //如果ROB[i]确实是第一个ROB条目，那么这个条目可以写回
                if (i==rob_head) begin
                    alu_real_reg_no = rob_real_no[i];
                    alu_logic_reg_no = rob_logic_no[i];
                    //更新物理寄存器
                    real_reg[alu_real_reg_no] = rob_value[i];
                    real_reg_ok[alu_real_reg_no] = 1;
                    //将当前块的下标改成line_head
                    //line_head[alu_logic_reg_no] = real_mapping_index_no[alu_real_reg_no];
                    //判断在映射表中，当前的块前有没有未写回的块
                    for (j=0; j<32; j=j+1) begin
                        if (mapping[alu_logic_reg_no][j]==alu_real_reg_no && mapping_use[alu_logic_reg_no][j]==1) begin
                            if (j==(line_head[alu_logic_reg_no]+1)%16) begin
                                //将前一个块释放掉，块号加入到空闲队列
                                free_tail = (free_tail+1)%64;
                                real_free_queue[free_tail] = mapping[alu_logic_reg_no][j-1];
                                mapping_use[alu_logic_reg_no][j-1] = 0;
                                //将链头后移，更新链的长度
                                line_head[alu_logic_reg_no] = (line_head[alu_logic_reg_no]+1)%16;
                                //$display("更新寄存器: ", alu_logic_reg_no, " 头指针: ", line_head[alu_logic_reg_no], " 尾指针: ", line_tail[alu_logic_reg_no], " 更新完链长度: ", line_tail[alu_logic_reg_no]-line_head[alu_logic_reg_no]);
                                //$display("下标为: ", j, " 更新寄存器: ", alu_logic_reg_no, " 更新完的队列尾指针: ", line_tail[alu_logic_reg_no], " 头指针: ", line_head[alu_logic_reg_no]);
                            end
                        end
                    end
                    
                    //$display("释放ROB: ", i);
                    //释放掉当前的ROB条目
                    rob_head=(rob_head+1)%32;
                    rob_ok[i] = 0;
                    rob_use[i] = 0;
                end
            end
        end
    end
endmodule